#include "midcode.h"
#include "table.h"

int midcode_table_p = 0;
MidCode midcode_table[MIDCODE_NUM] = {0};
int func_mid_map[MAX_FUNC_NUM][2] = {0};

Operand fallback;

const char OpcodeString[][STRING_LEN] = {
    "ADD_OP", "MINUS_OP", "MUL_OP", "DIV_OP",
    "IFLT_OP", "IFLET_OP", "IFMT_OP", "IFMET_OP",
    "IFNE_OP", "IFEQ_OP", "FUNC_OP",
    "ASSIGN_OP", "JMP_OP", "PARA_OP", "CALL_OP", "SCANF_OP", "PRINTF_OP",
    "RETV_OP", "RETN_OP",
};

const char OperandTypeString[][STRING_LEN] = {
    "NUM_MB", "STR_MB", "LVAR_MB", "GLOBAL_MB", "FUNC_MB", "REG_MB", "TMPVAR_MB", "JUMP_MB",
};

void midcode_init()
{
    fallback.operandType = NUM_MB;
    fallback.operandCont.constOp = 0;
}
void enter_midcode(int addr, Opcode op0, Operand op1, Operand op2, Operand op3)
{
    int i = addr;
    midcode_table[i].op0 = op0;
    midcode_table[i].op1 = op1;
    midcode_table[i].op2 = op2;
    midcode_table[i].op3 = op3;
    if (op0 == FUNC_OP) {
        func_mid_map[func_table_p - 1][0] = midcode_table_p;
        if (func_table_p >= 1) {
            func_mid_map[func_table_p - 2][1] = midcode_table_p - 1;
        }
    }
    if (i == midcode_table_p) {
        midcode_table_p++;
    }
}

void mid_move_forward(int step)
{
    midcode_table_p += step;
}

void set_last_map()
{
    func_mid_map[func_table_p - 1][1] = midcode_table_p - 1;
}
// @param type: 1 NormalElement; 0 FuncElement

//typedef enum Class {
//    GLOBAL_VAR = 100, GLOBAL_CONST, LOCAL_VAR, LOCAL_CONST, FUNC_RET, FUNC_REG, PARAMETER, TEMP_VAR,
//    // Extra Class for the convinence of set_param_for_table().
//    C_VAR, C_CONST,
//} Class;
//
//typedef enum OperandType {
//    NUM_MB, STR_MB, LVAR_MB, GLOBAL_MB, FUNC_MB, REG_MB, TMPVAR_MB,
//} OperandType;

void set_operand(Operand *op, void *em, ContType type)
{
    NormalElement *ne;
    FuncElement *fe;
    int ct;
    char *st;
    switch (type) {
        case NORMAL_CONT:
            ne = (NormalElement *)em;
            if (ne->clss == GLOBAL_VAR) {
                op->operandType = GLOBAL_MB;
                op->operandCont.varOp = ne;
            }
            else if (ne->clss == GLOBAL_CONST || ne->clss == LOCAL_CONST) {
                op->operandType = NUM_MB;
                // Attention here
                op->operandCont.constOp = (int)ne->value.int_val;
            }
            else if (ne->clss == LOCAL_VAR || ne->clss == PARAMETER) {
                op->operandType = LVAR_MB;
                op->operandCont.varOp = ne;
            }
            else if (ne->clss == TEMP_VAR) {
                op->operandType = TMPVAR_MB;
                op->operandCont.tmpOp = ne;
            }
            else {
                ;
            }
            break;
        case FUNC_CONT:
            fe = (FuncElement *)em;
            op->operandType = FUNC_MB;
            op->operandCont.funcOp = fe;
            break;
        case STRING_CONT:
            st = (char *)em;
            op->operandType = STR_MB;
            op->operandCont.stringOp = (char *)malloc(sizeof(*st));
            strcpy(op->operandCont.stringOp, st);
            break;
        case JUMP_CONT:
            ct = *(int *)em;
            op->operandType = JUMP_MB;
            op->operandCont.midcOp = ct;
        default:
            break;
    }
}

void set_const_operand(Operand *op, Value *em, TokenType type)
{
    int ct;
    switch (type) {
        case C_CHAR:
            ct = (int)(em->char_val);
            break;
        case C_INT:
            ct = (int)(em->int_val);
            break;
        case C_FLOAT:
            ct = (int)(em->float_val);
            break;
        default:
            ct = 0;
            break;
    }
    op->operandType = NUM_MB;
    op->operandCont.constOp = ct;
}